using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace GraphFS
{
	public class FSAccess : IAccess
	{

		private Dictionary<Int64, Int64> nodesIndex = new Dictionary<Int64, Int64>();
		private Dictionary<Int64, KeyValuePair<Int16, Int64>> nodesNeighborsIndex = new Dictionary<Int64, KeyValuePair<Int16, Int64>>();
		private string mapName;
		private FileStream nodeDataFs;
		private BinaryReader nodeDataBr;
		private FileStream nodeNeighborsDataFs;
		private BinaryReader nodeNeighborsDataBr;
		private FSNodesCache nodesCache;


		public FSAccess(string mapName)
		{
			this.mapName = mapName;
			Initialize();
		}




		private void Initialize()
		{
			nodesCache = new FSNodesCache();
			LoadNodesIndex();
			InitializeStreams(mapName);
			//getBounds();
		}

		private void InitializeStreams(string _mapName)
		{
			nodeDataFs = new FileStream(_mapName + ".nodes.data", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
			nodeDataBr = new BinaryReader(nodeDataFs);
			nodeNeighborsDataFs = new FileStream(_mapName + ".neighbors.data", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
			nodeNeighborsDataBr = new BinaryReader(nodeNeighborsDataFs);
		}
		public GeoRectangle GetBounds()
		{
			GeoRectangle ret;
			double latMin = double.PositiveInfinity;
			double latMax = double.NegativeInfinity;
			double lonMin = double.PositiveInfinity;
			double lonMax = double.NegativeInfinity;

			double lat;
			double lon;

			foreach (KeyValuePair<Int64, Int64> node in nodesIndex)
			{
				nodeDataBr.BaseStream.Seek(node.Value, SeekOrigin.Begin);
				lat = nodeDataBr.ReadDouble();
				lon = nodeDataBr.ReadDouble();
				latMin = Math.Min(latMin, lat);
				latMax = Math.Max(latMax, lat);
				lonMin = Math.Min(lonMin, lon);
				lonMax = Math.Max(lonMax, lon);
			}
			ret = new GeoRectangle(new GeoPoint(latMin, lonMin), new GeoPoint(latMax, lonMax));
			Console.WriteLine("{0} {1} {2} {3}", latMin,lonMin,latMax,lonMax);

			return ret;
		}


		private void LoadNodesIndex()
		{
			FileStream nodeIndexFs = new FileStream(mapName + ".nodes.index", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
			BinaryReader nodeIndexBr = new BinaryReader(nodeIndexFs);

			while (nodeIndexBr.BaseStream.Position < nodeIndexBr.BaseStream.Length)
			{
				Int64 nodeId = nodeIndexBr.ReadInt64();
				Int64 nodeOffset = nodeIndexBr.ReadInt64();
				nodesIndex.Add(nodeId, nodeOffset);
			}
			nodeIndexBr.Close();
			Console.WriteLine(nodesIndex.Count);


			FileStream nodeNeighborsIndexFs = new FileStream(mapName + ".neighbors.index", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
			BinaryReader nodeNeighborsIndexBr = new BinaryReader(nodeNeighborsIndexFs);

			while (nodeNeighborsIndexBr.BaseStream.Position < nodeNeighborsIndexBr.BaseStream.Length)
			{

				Int64 nodeId = nodeNeighborsIndexBr.ReadInt64();
				Int16 nCount = nodeNeighborsIndexBr.ReadInt16();
				Int64 offset = nodeNeighborsIndexBr.ReadInt64();
				nodesNeighborsIndex.Add(nodeId, new KeyValuePair<short, long>(nCount, offset));

			}

			Console.WriteLine("COUNTS {0}, {1}",nodesIndex.Count, nodesNeighborsIndex.Count);
			nodeNeighborsIndexBr.Close();



		}







		public FSNode GetNode(Int64 nodeId)
		{
			if (nodesIndex.ContainsKey(nodeId))
			{
				if (nodesCache.ContainsKey(nodeId))
				{
					return nodesCache [nodeId];
				}
				else
				{
					nodeDataBr.BaseStream.Seek(nodesIndex [nodeId], SeekOrigin.Begin);
					double lat = nodeDataBr.ReadDouble();
					double lon = nodeDataBr.ReadDouble();
					FSNode ret = new FSNode(nodeId, lat, lon);
					nodesCache.Add(nodeId, ret);
					return ret;
				}
			}
			return null;
		}


		public List<FSNode> GetNeighbors(Int64 nodeId)
		{
			if (nodesNeighborsIndex.ContainsKey(nodeId))
			{
				List<FSNode> ret = new List<FSNode>();
				nodeNeighborsDataBr.BaseStream.Seek(nodesNeighborsIndex [nodeId].Value, SeekOrigin.Begin);
				for (int i = 0; i < nodesNeighborsIndex [nodeId].Key; i++)
				{
					ret.Add(GetNode(nodeNeighborsDataBr.ReadInt64()));
				}
				return ret;
			}
			return null;
		}

		public List<FSNode> GetNodes(GeoRectangle rect)
		{
			return GetNodes(rect, 1);
		}



		public List<FSNode> GetNodes(GeoRectangle rect, int lod)
		{
			List<FSNode> ret = new List<FSNode>();
			FSNode n=null;
			foreach (KeyValuePair<Int64, Int64> node in nodesIndex)
			{
				//nodeDataBr.BaseStream.Seek(node.Value, SeekOrigin.Begin);
				n = GetNode(node.Key);
				if (rect.Contains(n.Location))
				{
					ret.Add(n);
				}
			}
			return ret;
		}




		#region IDisposable Members

		public void Dispose()
		{
			((IDisposable) this).Dispose();
		}

		#endregion

		#region IDisposable Members

		void IDisposable.Dispose()
		{
			this.nodeDataBr.Close();


		}

		#endregion
	}




}
